﻿using Orchard;
using Orchard.ContentManagement;
using Orchard.ContentManagement.MetaData;
using Orchard.Core.Contents.Extensions;
using Orchard.Data;
using Orchard.Data.Migration;
using Orchard.Environment.Configuration;
using Orchard.Logging;
using System;
using System.Data;
using Vitus.GoogleCalendar.Models;


namespace Vitus.GoogleCalendar
{
    public class Migrations : DataMigrationImpl
    {
        private readonly IOrchardServices orchardServices;

        private readonly ShellSettings shellSettings;

        private readonly ISessionFactoryHolder sessionFactoryHolder;

        public Migrations(
            IOrchardServices orchardServices,
            ShellSettings shellSettings,
            ISessionFactoryHolder sessionFactoryHolder)
        {
            if (orchardServices == null)
            {
                throw new ArgumentNullException("orchardServices");
            }

            if (shellSettings == null)
            {
                throw new ArgumentNullException("shellSettings");
            }

            if (sessionFactoryHolder == null)
            {
                throw new ArgumentNullException("sessionFactoryHolder");
            }

            this.orchardServices = orchardServices;
            this.shellSettings = shellSettings;
            this.sessionFactoryHolder = sessionFactoryHolder;
        }

        public ILogger Logger { get; set; }

        public int Create()
        {
            ContentDefinitionManager.AlterPartDefinition("GoogleCalendarPart", builder => builder
                .Attachable()
                .WithDescription("Displays a read-only Google calendar using jQuery Full Calendar plugin."));

            ContentDefinitionManager.AlterTypeDefinition("GoogleCalendarWidget",
                cfg => cfg
                    .WithPart("WidgetPart")
                    .WithPart("CommonPart")
                    .WithPart("IdentityPart")
                    .WithPart("GoogleCalendarPart")
                    .WithSetting("Stereotype", "Widget")
                );
            
            return 3;
        }

        public int UpdateFrom1()
        {
            SchemaBuilder
                .AlterTable("GoogleCalendarPartRecord", table =>
                    table.AlterColumn("GoogleCalendarUrls", col => col.WithType(DbType.String).WithLength(2000)))
                .AlterTable("GoogleCalendarPartRecord", table =>
                    table.AlterColumn("GoogleCalendarClasses", col => col.WithType(DbType.String).WithLength(400)))
                ;

            return 2;
        }

        public int UpdateFrom2()
        {
            var googleCalendarTable = this.GetPrefixedTableName("Vitus_GoogleCalendar_GoogleCalendarPartRecord");

            this.ExecuteReader("SELECT * FROM " + googleCalendarTable,
                (reader, connection) =>
                {
                    int lastContentItemId = (int)reader["Id"];
                    var googleCalendarPart = this.orchardServices.ContentManager.Get(lastContentItemId).As<GoogleCalendarPart>();

                    googleCalendarPart.GoogleCalendarIds = ConvertToString(reader["GoogleCalendarUrls"]);
                    googleCalendarPart.GoogleCalendarClasses = ConvertToString(reader["GoogleCalendarClasses"]);
                    googleCalendarPart.Theme = (bool)reader["Theme"];
                    googleCalendarPart.DefaultView = (FullCalendarDefaultView)Enum.Parse(typeof(FullCalendarDefaultView), (string)reader["DefaultView"]);
                    googleCalendarPart.HeaderLeft = ConvertToString(reader["HeaderLeft"]);
                    googleCalendarPart.HeaderCenter = ConvertToString(reader["HeaderCenter"]);
                    googleCalendarPart.HeaderRight = ConvertToString(reader["HeaderRight"]);
                    googleCalendarPart.Weekends = (bool)reader["Weekends"];
                    googleCalendarPart.WeekNumbers = (bool)reader["WeekNumbers"];
                    googleCalendarPart.AllDaySlot = (bool)reader["AllDaySlot"];
                    googleCalendarPart.MinTime = (byte)reader["MinTime"];
                    googleCalendarPart.MaxTime = (byte)reader["MaxTime"];
                });

            SchemaBuilder.DropTable("GoogleCalendarPartRecord");

            return 3;
        }

        private string GetPrefixedTableName(string tableName)
        {
            if (string.IsNullOrWhiteSpace(this.shellSettings.DataTablePrefix))
            {
                return tableName;
            }

            return this.shellSettings.DataTablePrefix + "_" + tableName;
        }

        private void ExecuteReader(string sqlStatement, Action<IDataReader, IDbConnection> action)
        {
            using (var session = this.sessionFactoryHolder.GetSessionFactory().OpenSession())
            {
                var command = session.Connection.CreateCommand();
                command.CommandText = string.Format(sqlStatement);

                var reader = command.ExecuteReader();

                while (reader != null && reader.Read())
                {
                    try
                    {
                        if (action != null)
                        {
                            action(reader, session.Connection);
                        }
                    }
                    catch (Exception e)
                    {
                        Logger.Error(e, "Error while executing custom SQL Statement in Upgrade.");
                    }
                }

                if (reader != null && !reader.IsClosed)
                {
                    reader.Close();
                }
            }
        }

        private static string ConvertToString(object readerValue)
        {
            return readerValue == DBNull.Value ? null : (string)readerValue;
        }
    }
}